/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package analex;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedList;

/**
 * A Split procura encontrar uma cadeia de carateres que possam forma um lexema
 * válido. Ela tambem sobre qual caminho o automato vai seguir
 * Ela equivale ao estado inícial do automato.
 *
 * @author Pedrom
 */
public class Spliter {

    private LinkedList<Token> listaToken;
    private LinkedList<Erro> listaErro;

    public Spliter() {
        this.listaToken = new LinkedList<>();
        this.listaErro = new LinkedList<>();
    }

    public void split(String code) throws IOException {
        BufferedReader bf = new BufferedReader(new StringReader(code));
        String linha = bf.readLine();

        Operators op = new Operators();        
        //StringBuilder lexema = new StringBuilder();
        char charAtual;
        int current, current_line = 1;
        //Enquanto não chegar o fim do ...
        while (linha != null) {
            current = 0; //Zera a leitura de colunas.
            //Enquanto não chegar ao fim da linha...
            while (current < linha.length()) {
                charAtual = linha.charAt(current);
                // Consume o espaço em branco e tabulação.
                if (charAtual == ' ' || charAtual == '\t') {
                    current++;
                    continue;
                }

                //Verificar se o caracter pertence à linguagem
                int ascii = (int) charAtual;
                if (ascii < 32 || ascii > 126) {
                    current++;
                    Erro er = new Erro(String.valueOf(charAtual), "Símbolo não pertecente", current_line);
                    System.out.println("Símbolo não pertecente");
                    listaErro.add(er);
                    continue;
                }

                // checa se pode ser operador
                char charNext = ' ';
                if (current + 1 < linha.length()) {
                    charNext = linha.charAt(current + 1);
                }
                // Entra no estado de operator somente se não houver a condição -(digit), que vai ser um numero.
                if (op.canBeOperator(String.valueOf(charAtual)) && (!Character.isDigit(charNext) || charAtual != '-')) {

                    FsmOperator fsm;
                    StringBuilder lexema = new StringBuilder();
                    lexema.append(charAtual);

                    //Tratamento de comentário                    
                    if (charAtual == '/' && (charNext == '/' || charNext == '*')) {
                        if (charNext == '/') {
                            //System.out.println("Apagou Commente");
                            break; // Desconsidera a linha. Saí do while(current < linha.length()).
                        } else {
                            while (charAtual != '*' || charNext != '/') {
                                current++;
                                charAtual = linha.charAt(current);
                                if (current + 1 == linha.length()) { // Acabou a linha e não fechou o comentário.
                                    linha = bf.readLine();
                                    current = 0;
                                    if (linha == null) {
                                        Erro er = new Erro(String.valueOf(charAtual), "falta fechamento de comentario", current_line);
                                        //System.out.println("Comentario sem fechar "+current_line);
                                        return; // Chegou ao final do código fonte e não encontrou o fim do cometário.
                                    }
                                }
                                if (charAtual == '*' && current + 1 < linha.length()) {
                                    charNext = linha.charAt(current + 1);
                                }
                            }
                            //System.out.println("Apagou /**/");
                            current = current + 2;
                            continue;
                        }
                    }
                    //Checa se é operador
                    if (op.canBeOperator(String.valueOf(charNext))) {
                        lexema.append(charNext);
                        fsm = new FsmOperator(String.valueOf(charAtual), String.valueOf(charNext));
                        current++;
                    } else {
                        fsm = new FsmOperator(String.valueOf(charAtual));
                    }
                    if (fsm.isOperator()) {
                        Token tk = new Token("operator", lexema.toString(), current_line);
                        listaToken.add(tk);
                        System.out.println("Op: " + lexema);
                    } else {
                        Erro er = new Erro(lexema.toString(), "Erro de Operador", current_line);
                        listaErro.add(er);
                        System.out.println("Erro Op: " + lexema);
                    }
                    current++;
                    continue;
                }

                //Checar se é delimiter
                Delimiters deli = new Delimiters();
                if (deli.isDelimiter(String.valueOf(charAtual))) {
                    Token tk = new Token("delimiter", String.valueOf(charAtual), current_line);
                    listaToken.add(tk);
                    System.out.println("Delimiter: " + charAtual);
                    current++;
                    continue;
                }

                //Checar se é String                
                if (charAtual == '"') {
                    StringBuilder lexema = new StringBuilder();
                    lexema.append(charAtual);
                    current++;
                    if (current + 1 < linha.length()) {
                        charAtual = linha.charAt(current);
                    } else {
                        Erro er = new Erro(lexema.toString(), "Erro de String", current_line);
                        System.out.println("Erro String: " + lexema);
                        continue;
                    }
                    // Verifica a linha inteira pelo fechamento da String
                    while (charAtual != '"') {
                        lexema.append(charAtual);
                        current++;
                        if (current >= linha.length()) {
                            //Erro er = new Erro(lexema.toString(), "Erro de String", current_line);
                            //System.out.println("Erro String: " + lexema);
                            //listaErro.add(er);
                            break;
                        } else {
                            charAtual = linha.charAt(current);
                        }
                    }
                    lexema.append(charAtual);
                    FsmString fsm = new FsmString();
                    if (fsm.isString(lexema.toString())) {
                        Token tk = new Token("String", lexema.toString(), current_line);
                        listaToken.add(tk);
                        System.out.println("String: " + lexema);
                    } else {
                        Erro er = new Erro(lexema.toString(), "Erro de String", current_line);
                        System.out.println("Erro String: " + lexema);
                    }
                    current++;
                    continue;
                }

                //Checar se é Character
                if (charAtual == '\'') {
                    StringBuilder lexema = new StringBuilder();
                    lexema.append(charAtual);
                    current++;
                    if (current < linha.length()) {
                        charAtual = linha.charAt(current);
                    } else {
                        Erro er = new Erro(lexema.toString(), "Erro de Character", current_line);
                        listaErro.add(er);
                        System.out.println("Erro char: " + lexema);
                        continue;
                    }
                    //Verifica a linha inteira pelo ' de fechada de char.
                    while (charAtual != '\'') {
                        lexema.append(charAtual);
                        current++;
                        if (current >= linha.length()) {
                            //Erro er = new Erro(lexema.toString(), "Erro de Character", current_line);
                            //listaErro.add(er);
                            break;
                        } else {
                            charAtual = linha.charAt(current);
                        }
                    }
                    lexema.append(charAtual);
                    FsmCharacter fsm = new FsmCharacter();
                    if (fsm.isCharacter(lexema.toString())) {
                        Token tk = new Token("character", lexema.toString(), current_line);
                        listaToken.add(tk);
                        System.out.println("char: " + lexema);
                    } else {
                        Erro er = new Erro(lexema.toString(), "Erro de Character", current_line);
                        listaErro.add(er);
                        System.out.println("Erro char: " + lexema);
                    }
                    current++;
                    continue;
                }

                //Checar se é Identifier                
                if (Character.isLetter(charAtual) || charAtual == '_') {
                    StringBuilder lexema = new StringBuilder();
                    lexema.append(charAtual);
                    current++;

                    if (current >= linha.length()) {
                        Token tk = new Token("Identifier", lexema.toString(), current_line);
                        listaToken.add(tk);
                        System.out.println("Id: " + lexema);
                        continue;
                    }

                    charAtual = linha.charAt(current);
                    if (op.containsValue(String.valueOf(charAtual)) || op.isOther(String.valueOf(charAtual)) || deli.isDelimiter(String.valueOf(charAtual))) {
                        Token tk = new Token("Identifier", lexema.toString(), current_line);
                        listaToken.add(tk);
                        System.out.println("Id: " + lexema);
                        continue;
                    }
                    StringBuilder mark;
                    while (charAtual != ' ' && current < linha.length()) {

                        lexema.append(charAtual);
                        current++;
                        if (current < linha.length()) {
                            charAtual = linha.charAt(current);
                        }
                        mark = new StringBuilder();
                        if (op.containsValue(String.valueOf(charAtual)) || op.isOther(String.valueOf(charAtual)) || deli.isDelimiter(String.valueOf(charAtual))) {
                            break;// Sai se o primeiro operador sozinho for um operando Single
                        }
                        if (current + 1 < linha.length()) {
                            charNext = linha.charAt(current + 1);
                            mark.append(charAtual);
                            mark.append(charNext);
                            if (op.containsValue(mark.toString())) {
                                break;// Sai se o os dois próximos caracters forem um operando composto.
                            }
                        }
                    }
                    FsmIdentifier fsm = new FsmIdentifier();
                    if (fsm.isIdentifier(lexema.toString())) {
                        symboltable symbols = new symboltable();
                        if (symbols.containsKey(lexema.toString())) {
                            Token tk = new Token("Reserved Word", lexema.toString(), current_line);
                            listaToken.add(tk);
                            System.out.println("rw: " + lexema);
                        } else {
                            Token tk = new Token("Identifier", lexema.toString(), current_line);
                            listaToken.add(tk);
                            System.out.println("Id: " + lexema);
                        }
                    } else {
                        Erro er = new Erro(lexema.toString(), "Erro de Identificador", current_line);
                        listaErro.add(er);
                        System.out.println("erro Id:" + lexema);

                    }
                    continue;
                }

                //Checar se é Number
                // Se o char '-' chegou e não foi tratado pelo estado de operador é porque é um numero.
                if (Character.isDigit(charAtual) || charAtual == '-') {
                    StringBuilder lexema = new StringBuilder();
                    lexema.append(charAtual);
                    current++;

                    if (current >= linha.length()) {
                        Token tk = new Token("Number", lexema.toString(), current_line);
                        listaToken.add(tk);
                        System.out.println("Num: " + lexema);
                        continue;
                    }
                    //Se o proximo char for um operador ou delimitador ele para.
                    // Ex x=... ou x;
                    charAtual = linha.charAt(current);
                    if ((op.containsValue(String.valueOf(charAtual)) && charAtual != '.') || op.isOther(String.valueOf(charAtual)) || deli.isDelimiter(String.valueOf(charAtual))) {
                        Token tk = new Token("Number", lexema.toString(), current_line);
                        listaToken.add(tk);
                        System.out.println("Num: " + lexema);
                        continue;
                    }
                    StringBuilder mark;
                    while (charAtual != ' ' && current < linha.length()) {

                        lexema.append(charAtual);
                        current++;
                        if (current < linha.length()) {
                            charAtual = linha.charAt(current);
                        }
                        mark = new StringBuilder();

                        if ((op.containsValue(String.valueOf(charAtual)) && charAtual != '.') || op.isOther(String.valueOf(charAtual)) || deli.isDelimiter(String.valueOf(charAtual))) {
                            break;// Sai se o primeiro operador sozinho for um operando Single
                        }
                        if (current + 1 < linha.length()) {
                            charNext = linha.charAt(current + 1);
                            mark.append(charAtual);
                            mark.append(charNext);
                            if (op.containsValue(mark.toString()) && charAtual != '.') {
                                break;// Sai se o os dois próximos caracters forem um operando composto.
                            }
                        }
                    }
                    FsmNumber fsm = new FsmNumber();
                    if (fsm.isNumber(lexema.toString())) {
                        Token tk = new Token("Identifier", lexema.toString(), current_line);
                        listaToken.add(tk);
                        System.out.println("Num: " + lexema);
                    } else {
                        Erro er = new Erro(lexema.toString(), "Erro de Numero", current_line);
                        listaErro.add(er);
                        System.out.println("erro Num:" + lexema);
                    }
                    continue;
                }
            }
            current_line++;
            linha = bf.readLine();// Proxima linha.
        }
        System.out.println("Acabou texto");
    }

    public LinkedList<Token> getListaToken() {
        return listaToken;
    }

    public LinkedList<Erro> getListaErro() {
        return listaErro;
    }    
}
